package hujz.java.file_server.http;

import static hujz.java.file_server.Configuration.INSTANCE;
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;

import java.io.File;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import hujz.java.file_server.Configuration;
import hujz.java.file_server.FileMeta;
import hujz.java.file_server.NativeInterface;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelProgressiveFuture;
import io.netty.channel.ChannelProgressiveFutureListener;
import io.netty.channel.DefaultFileRegion;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;

public class HttpImageServerHandler extends ChannelInboundHandlerAdapter {

	private static final NativeInterface Native_Interface = new NativeInterface();
	private static final Logger LOGGER = LoggerFactory.getLogger(HttpImageServerHandler.class);
	private static File ImageResizeDir = new File(System.getProperty("storage.image.resize", "temp"));
//	static {
//		if (!ImageResizeDir.getParentFile().exists() && !ImageResizeDir.getParentFile().isDirectory()) {
//			if(!ImageResizeDir.getParentFile().mkdirs()) {
//				System.err.println("create dir \"" + ImageResizeDir.getParentFile().getAbsolutePath() + "\" failed, application will exit!");
//				System.exit(-1);
//			}
//		}
//		LOGGER.info("parent dir for image resize is \"{}\".", ImageResizeDir.getParentFile().getAbsolutePath());
//		// load .so
//	}

	private final HttpRequest request;
	private HttpResponse response;

	private final String uri;
	private final String contentType;
	private final String fileName;
	private final String fileId;
	private final String resize;

	public HttpImageServerHandler(HttpRequest request, HttpResponse response) {
		this.request = request;
		this.response = response;

		QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
		Map<String, List<String>> uriAttributes = decoderQuery.parameters();

		this.fileId = uriAttributes.get("fileId").get(0);
		this.resize = uriAttributes.get("resize").get(0);

		FileMeta fileMeta = INSTANCE.getFileMetaSaveImpl().get(fileId);
		this.uri = fileMeta.getUri();
		this.contentType = fileMeta.getMediaType();
		this.fileName = fileMeta.getFileName();
	}

	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (uri == null) {
			sendError(ctx, FORBIDDEN);
			return;
		}

		File file = Configuration.INSTANCE.getStorageImpl().file(uri);
		if (file.isDirectory() || !file.exists()) {
			sendError(ctx, NOT_FOUND);
			return;
		}
		if (!file.isFile()) {
			sendError(ctx, FORBIDDEN);
			return;
		}

        LOGGER.info("[FileServer] --> Start resize image, fileName: \"{}\".", fileName);
		
		int[] size = spiltSize(resize);
		String newFileName = fileId + "-" + size[0] + "x" + size[1];
		File newFile = new File(ImageResizeDir, newFileName);

        LOGGER.info("[FileServer] --- Start resize image, fileName: \"{}\".", newFile.getAbsolutePath());
		if(Native_Interface.resize(file.getAbsolutePath(), newFile.getAbsolutePath(), size[0], size[1]) == 1) {
	        LOGGER.info("[FileServer] --- Start send resized image, fileName: \"{}\".", newFile.getAbsolutePath());
            HttpUtil.setContentLength(response, newFile.length());
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, this.contentType);
            try {
                response.headers().add(HttpHeaderNames.CONTENT_DISPOSITION, "attachment;filename=\"" + new String(fileName.getBytes("utf-8"), "iso8859-1") + "\"");
            } catch (Exception e) {
                e.printStackTrace();
                // ignore
            }
            ctx.write(response);

            ChannelFuture sendFileFuture = ctx.write(new DefaultFileRegion(newFile, 0, newFile.length()), ctx.newProgressivePromise());
            ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

            sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
                @Override
                public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
                    if (total < 0) {
                        LOGGER.trace("[FileServer] --- Transfer progress: " + progress);
                    } else {
                        LOGGER.trace("[FileServer] --- Transfer progress: " + progress + " / " + total);
                    }
                }

                @Override
                public void operationComplete(ChannelProgressiveFuture future) {
                    LOGGER.info("[FileServer] <-- Transfer complete, fileName: \"{}\".", fileName);
                    future.channel().flush();
                }
            }).addListener(ChannelFutureListener.CLOSE);
            
		} else {
	        LOGGER.info("[FileServer] --- Resize image failed, fileName: \"{}\".", newFile.getAbsolutePath());
	        response.setStatus(NOT_FOUND);
	        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
		}

	}

	private int[] spiltSize(String resize) {
		if (resize == null || resize.trim().length() == 0)
			return new int[] { 200, 0 };
		int n = resize.indexOf('*');
		if (n == -1)
			return new int[] { Integer.parseInt(resize), 0 };
		else {
			String str1 = resize.substring(0, n), str2 = resize.substring(n + 1);
			return new int[] { str1.length() == 0 ? 0 : Integer.parseInt(str1), str2.length() == 0 ? 0 : Integer.parseInt(str2) };
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		sendError(ctx, INTERNAL_SERVER_ERROR);
        LOGGER.info("[FileServer] <-- Resize iamge failed, fileName: \"{}\"!", fileName);
		LOGGER.error(cause.getMessage(), cause);
	}

	private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
		response.setStatus(status);
		response = ((DefaultFullHttpResponse) response).replace(Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
		response.headers().set("Content-Type", "text/plain; charset=UTF-8");
		ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
	}

}
